function AppAssistant() {
	Mojo.Log.info('--- BEGIN AppAssistant()');
	
	this.closePreferences = false;
	this.fromLauncher = false;
	this.calendarEvents = new Array();
	this.existingCalendarEvents = new Array();
	this.calenderEventsToClose = new Array();
	this.calenderEventsToHide = new Array();

	this.refreshTimer = null;
	
	this.nowUTC = null;
	this.thenUTC = null;
	this.todayStartUTC = null;
}

AppAssistant.prototype.setup = function(){
	Mojo.Log.info('--- BEGIN setup()');
	
	try {
	    // create db
    	var dboptions = {name: "dbupdates", replace: false};
   		this.depot = new Mojo.Depot(dboptions, this.dbConnectionSuccess, this.dbConnectionFailure);
		
		// read preferences
    	//this.depot.simpleGet("preferences", this.getPreferencesOK.bind(this), function() {});

	} catch (e) {
		Mojo.Log.error('--- ERROR: AppAssistant.setup()', e);
	}
}

AppAssistant.prototype.getPreferencesOK = function(result) {
	Mojo.Log.info('--- BEGIN getPreferencesOK(), result: ' + result);
	
    if (result != null) {
		Preferences = result;
		var appController = Mojo.Controller.getAppController();
		var preferencesCardStageController = appController.getStageController("preferencesCardStage");
		preferencesCardStageController.delegateToSceneAssistant('updatePreferences');
	}
}

AppAssistant.prototype.dbConnectionSuccess = function(){  
    Mojo.Log.info('--- BEGIN dbConnectionSuccess()');
}

AppAssistant.prototype.dbConnectionFailure = function(transaction, result){  
    Mojo.Log.error('--- BEGIN dbConnectionFailure(): ' + result.message); 
}

//handleLaunch - called by the framework when the application is asked to launch
AppAssistant.prototype.handleLaunch = function(launchParams) {
	Mojo.Log.info("--- BEGIN AppAssistant.handleLaunch()");
	try {
		var appController = Mojo.Controller.getAppController();
		
		if (!launchParams) {
			// app was launched directly from the launcher
			Mojo.Log.info('--- AppAssistant.handleLaunch: Launched from App Launcher');
			this.fromLauncher = true;

			//this.openPreferences();
			this.getCalendars();
		}		
	} catch (e) {
		Mojo.Log.error('--- ERROR: AppAssistant.prototype.handleLaunch()', e);
	}
}


AppAssistant.prototype.cleanup = function() {
	try {
		Mojo.Log.info('--- BEGIN AppAssistant.cleanup()');
		
		// Close all stages
		Mojo.Controller.getAppController().closeAllStages();
	} catch (e) {
		Mojo.Log.error('--- ERROR: AppAssistant.cleanup()', e);
	}
}


AppAssistant.prototype.createPreferencesDashboard = function(icon, title, message){
	try {
		Mojo.Log.info('--- BEGIN AppAssistant.createPreferencesDashboard(', icon, title, message, ')');
		
		var appController = Mojo.Controller.getAppController();

		var dashboardStage = appController.getStageController('preferencesDashboardStage');
		if (dashboardStage) {
			Mojo.Log.info('--- AppAssistant.createPreferencesDashboard: Dashboard already existis, nothing to do');
		}
		else {
			Mojo.Log.info('--- AppAssistant.createPreferencesDashboard: createStageWithCallback: title: ' + title + ', message: ' + message);
			var pushDashboard = function(stageController) {
				Mojo.Log.info('--- AppAssistant.createPreferencesDashboard: stageController.pushScene: title: ' + title + ', message: ' + message);
				stageController.pushScene('dashboard', 'preferences', icon, title, message);
			};
			Mojo.Log.info('--- AppAssistant.createPreferencesDashboard: createStageWithCallback');
			this.controller.createStageWithCallback(
				{
					name: 'preferencesDashboardStage',
					lightweight: true,
					icon: icon
				}, 
				pushDashboard.bind(this),
				'dashboard'
			);
		}
	}
	catch (e) {
		Mojo.Log.error('--- ERROR: AppAssistant.createPreferencesDashboard()', e);
	}
}

AppAssistant.prototype.openPreferences = function() {
	Mojo.Log.info("--- BEGIN AppAssistant.openPreferences()");
	try {
		var appController = Mojo.Controller.getAppController();
		
		var preferencesCardStageController = appController.getStageController("preferencesCardStage");
		if (preferencesCardStageController) {
			preferencesCardStageController.swapScene('preferences', this);
			preferencesCardStageController.activate();
		}
		else {
			var pushCardScene = function(stageController){
				Mojo.Log.info('--- AppAssistant.openPreferences.pushCardScene: stageController: ' + stageController);
				stageController.swapScene('preferences', this);
			}
			var stageArguments = {
				name: "preferencesCardStage",
				lightweight: true
			};
			appController.createStageWithCallback(stageArguments, pushCardScene.bind(this), 'card');
		}
	} catch (e) {
		Mojo.Log.error("--- ERROR: AppAssistant.openPreferences", e);
	}	
}

AppAssistant.prototype.setRefreshTimerInterval = function() {
	Mojo.Log.info("--- BEGIN AppAssistant.setRefreshTimerInterval()");
	try {
		// cancel refresh timer
		var appController = Mojo.Controller.getAppController();
		var preferencesDashboardStageController = appController.getStageController("preferencesDashboardStage");
		Mojo.Log.info("--- AppAssistant.setRefreshTimerInterval: preferencesDashboardStageController: " + preferencesDashboardStageController + ", this.refreshTimer: " + this.refreshTimer);
		if (this.refreshTimer != null) {
			Mojo.Log.info("--- AppAssistant.setRefreshTimerInterval: clearTimeout");
			preferencesDashboardStageController.window.clearInterval(this.refreshTimer);
		}
		
		// set new refresh timer
		var timeToRefresh = (Preferences.refreshHours * 60 + Preferences.refreshMinutes) * 60 * 1000;
		this.refreshTimer = preferencesDashboardStageController.window.setInterval(this.refreshCalendarEvents.bind(this), timeToRefresh);
		Mojo.Log.error("--- AppAssistant.setRefreshTimerInterval: timeToRefresh: " + timeToRefresh);
				
	} catch (e) {
		Mojo.Log.error("--- ERROR: AppAssistant.setRefreshTimerInterval", e);
	}
}

AppAssistant.prototype.refreshCalendarEvents = function(closePreferences) {
	Mojo.Log.error("--- BEGIN AppAssistant.refreshCalendarEvents()");
	try {
		this.calcNowThenUTC();
	
		this.closePreferences = closePreferences;
		this.existingCalendarEvents = new Array();
		
		this.discardOldHideEvents();
		this.discardOldCalendarEvents();
		
		this.getCalendars();
		//this.getEvents();
	} catch (e) {
		Mojo.Log.error("--- ERROR: AppAssistant.refreshCalendarEvents", e);
	}
}

AppAssistant.prototype.discardOldHideEvents = function() {
	Mojo.Log.info("--- BEGIN AppAssistant.discardOldHideEvents()");
	try {
		// only refresh past events
		var appController = Mojo.Controller.getAppController();
		var newCalendarEventsToHide = new Array();
		
		for (var i = 0; i < this.calenderEventsToHide.length; i++) {
			
			var thisEvent = this.calenderEventsToHide[i];
			
			if (thisEvent.end >= this.nowUTC) {
				// keep events still in future
				newCalendarEventsToHide.push(thisEvent);
			}
		}
		
		this.calenderEventsToHide = newCalendarEventsToHide;
	}
	catch (e) {
		Mojo.Log.error("--- ERROR: AppAssistant.discardOldHideEvents", e);
	}
}

AppAssistant.prototype.discardOldCalendarEvents = function() {
	Mojo.Log.info("--- BEGIN AppAssistant.discardOldCalendarEvents()");
	try {
		// only refresh past events
		var appController = Mojo.Controller.getAppController();
		
		for (var i = 0; i < this.calendarEvents.length; i++) {
			
			var thisEvent = this.calendarEvents[i];
			
			Mojo.Log.info("--- AppAssistant.discardOldCalendarEvents: " + thisEvent);
			
			new Mojo.Service.Request('palm://com.palm.calendar', {
				method: 'getEvent',
				parameters: {
					id: thisEvent.id,
					subscribe: false,
				},
				onSuccess: this.getEventSuccess.bind(this, thisEvent, i),
				onFailure: this.getEventFailure.bind(this, thisEvent, i)
			});
		}
	}
	catch (e) {
		Mojo.Log.error("--- ERROR: AppAssistant.discardOldCalendarEvents", e);
	}
}

AppAssistant.prototype.getEventSuccess = function(thisEvent, index, result){
	Mojo.Log.info("--- BEGIN AppAssistant.getEventSuccess result: " + result + ", thisEvent: " + thisEvent);
	
	try {
		var appController = Mojo.Controller.getAppController();

		// event still exists, look for changes
		Mojo.Log.info("--- AppAssistant.getEventSuccess: event still exists: " + thisEvent.subject);
		Mojo.Log.info("--- AppAssistant.getEventSuccess: result.startTimestamp: " +
				new Date(result.startTimestamp).toUTCString() + ", thenUTC: " + new Date(this.thenUTC).toUTCString());
		Mojo.Log.info("--- AppAssistant.getEventSuccess: result.endTimestamp: " +
				new Date(result.endTimestamp).toUTCString() + ", nowUTC: " + new Date(this.nowUTC).toUTCString());

		if (this.isEventToHide(result)) {
			// event passed or not to show
			Mojo.Log.info("--- AppAssistant.getEventSuccess: event has past: " + thisEvent.subject);
			this.calenderEventsToClose.push(thisEvent);
			Mojo.Log.info("--- AppAssistant.getEventSuccess: hinter closeStage");
		}
		else {
			Mojo.Log.info("--- AppAssistant.getEventSuccess: event in future: " + thisEvent.subject);
			thisEvent.start = result.startTimestamp;
			thisEvent.end = result.endTimestamp;
			thisEvent.location = result.location;
			thisEvent.calendarId = result.calendarId;
			thisEvent.subject = result.subject;
			thisEvent.note = result.note;
			thisEvent.allDay = result.allDay;
			thisEvent.id = result.reminderLongId;
			this.existingCalendarEvents.push(thisEvent);
			
			var dashboardStage = appController.getStageController(thisEvent.getShortId());
			dashboardStage.delegateToSceneAssistant('updateCalendarEvent');
		}
	}
	catch (e) {
		Mojo.Log.error("--- ERROR: AppAssistant.getEventSuccess", e);
	}	
}

AppAssistant.prototype.getEventFailure = function(thisEvent, index, result){
	Mojo.Log.info("--- AppAssistant.getEventFailure result: " + result + ", thisEvent: " + thisEvent);
	
	try {
		// event is gone, so delete it
		Mojo.Log.info("--- AppAssistant.getEventFailure: event is gone: " + thisEvent.subject);
		this.calenderEventsToClose.push(thisEvent);
	}
	catch (e) {
		Mojo.Log.error("--- ERROR: AppAssistant.getEventSuccess", e);
	}	
}

AppAssistant.prototype.getEvents = function() {
		Mojo.Log.info("--- BEGIN AppAssistant.getEvents(): Get Events from",new Date(this.nowUTC).toUTCString(),":",new Date(this.thenUTC).toUTCString());
	
	try {
		// get calendar events
		new Mojo.Service.Request('palm://com.palm.calendar', {
			method: 'getEvents',
			parameters: {
				"calendarId": "all",
				"startDate": this.nowUTC,
				"endDate": this.thenUTC,
				"subscribe": false,
				"freeTimes": true
			},
			onSuccess: this.gotMyEvents.bind(this),
			onFailure: function(resp){
				Mojo.Log.info("Failure!!!!!!! " + resp.errorCode + " " + resp.errorText);
				Mojo.Controller.errorDialog("Failure!!!!!!! Cannot read Events.");
			}
		});
	} catch (e) {
		Mojo.Log.error("--- ERROR: AppAssistant.getEvents", e);
	}
}

AppAssistant.prototype.gotMyEvents = function(resp) {
	Mojo.Log.info("--- BEGIN AppAssistant.gotMyEvents");

	try {
		var appController = Mojo.Controller.getAppController();

		// still existing events after clean events in past and future		
		this.calendarEvents = this.existingCalendarEvents;
	
		var days = resp.days.length;
		Mojo.Log.info("--- AppAssistant.gotMyEvents: Found events for " + days + " days.");
		
		// loop through days
		for (var day = 0; day < days; day++) {
			
			// all day events
			for (var event = 0; event < resp.days[day].allDayEvents.length; event++) {
			
				var thisEvent = resp.days[day].allDayEvents[event];
				
				Mojo.Log.info("--- AppAssistant.gotMyEvents: found allDay event: id: " + thisEvent.id +
					", subject: " + thisEvent.subject +	", start: " + thisEvent.start + ", end: " +	thisEvent.end);
				
				// check if event is to hide
				if (this.isEventToHide(thisEvent)) {
					continue;
				}
				
				// if thisEvent already in calendarEvents, only update start/end
				this.createOrUpdateEvent(thisEvent);
			}

			// timed events
			for (var event = 0; event < resp.days[day].events.length; event++) {
								
				var thisEvent = resp.days[day].events[event];
	
				Mojo.Log.info("--- AppAssistant.gotMyEvents: found timed event: id: " + thisEvent.id + 
					", subject: " + thisEvent.subject +	", start: " + thisEvent.start +	", end: " + thisEvent.end);
				
				// check if event is to hide
				if (this.isEventToHide(thisEvent)) {
					continue;
				}
				
				// if thisEvent already in calendarEvents, only update start/end
				this.createOrUpdateEvent(thisEvent);
			}
		}
		
		if (this.closePreferences) {
			appController.closeStage('preferencesCardStage');
			this.closePreferences = false;	
		}

		// close all dashboard stages for past or non existing events
		for (var i = 0; i < this.calenderEventsToClose.length; i++) {
			var thisEvent = this.calenderEventsToClose[i];
			appController.closeStage(thisEvent.getShortId());
		}
		this.calenderEventsToClose = new Array();
		
	} catch (e) {
		Mojo.Log.error("--- ERROR: AppAssistant.gotMyEvents", e);
	}
}

AppAssistant.prototype.isEventToHide = function(thisEvent) {
	Mojo.Log.info('--- BEGIN AppAssistant.isEventToHide()');
	try {
	
		// check event limit	
		if (this.eventLimitReached(this.existingCalendarEvents)) {
			return true;
		}
				// check for time stamps
		if ((thisEvent.endTimestamp < (Preferences.showPassedEvents ? this.todayStartUTC : this.nowUTC)) ||
				(thisEvent.startTimestamp > this.thenUTC)) {
			return true;
		}
		
		// check preferences
		if ((thisEvent.allDay == true && Preferences.showAllDayEvents == false) ||
				(thisEvent.allDay == false && Preferences.showTimedEvents == false)) {
			return true;
		}
			
		// check if event is on hide events list
		for (var i = 0; i < this.calenderEventsToHide.length; i++) {
			var hideEvent = this.calenderEventsToHide[i];
			if (hideEvent.id == thisEvent.id) {
				// event should be hide, so do nothing
				Mojo.Log.info("--- AppAssistant.isEventToHide: " + hideEvent.id);
				return true;
			}
		}
		
		// check if event.calendar is on calendars to hide list
		for (var i = 0; i < Preferences.calendarsToHide.length; i++) {
			var hideCalendar = Preferences.calendarsToHide[i];
			if (thisEvent.calendarId == hideCalendar) {
				return true;
			}
		}		
		
		return false;
	}
	catch (e) {
		Mojo.Log.error('--- ERROR: AppAssistant.isEventToHide()', e);
	}
}

AppAssistant.prototype.createOrUpdateEvent = function(thisEvent) {
	Mojo.Log.info('--- BEGIN AppAssistant.createOrUpdateEvent()');
	try {
		var thisCalendarEvent = new CalendarEvent(thisEvent.id, thisEvent.start, thisEvent.end,
				thisEvent.location,	thisEvent.calendarId, thisEvent.subject, thisEvent.note, thisEvent.allDay);
		this.createCalendarEventDashboard("images/date_1.png", thisCalendarEvent);
	}
	catch (e) {
		Mojo.Log.error('--- ERROR: AppAssistant.createOrUpdateEvent()', e);
	}
}

AppAssistant.prototype.createCalendarEventDashboard = function(icon, calendarEvent) {
	Mojo.Log.info('--- BEGIN AppAssistant.createCalendarEventDashboard(', icon, title, message, ')');
	try {
		
		var appController = Mojo.Controller.getAppController();
		var calendarEventDashboardStage = calendarEvent.getShortId();
		
		var title = calendarEvent.getDashboardTitle();
		var message = calendarEvent.getDashboardMessage();
		
		var dashboardStage = appController.getStageController(calendarEventDashboardStage);
		if (dashboardStage) {
			Mojo.Log.info('--- AppAssistant.createCalendarEventDashboard: Dashboard already existis, nothing to do');
		}
		else {
			Mojo.Log.error('--- AppAssistant.createCalendarEventDashboard: createStageWithCallback: title: ' + title + ', message: ' + message);
			var pushDashboard = function(stageController) {
				Mojo.Log.error('--- AppAssistant.createCalendarEventDashboard: stageController.pushScene: title: ' + title + ', message: ' + message);
				stageController.pushScene('dashboard', 'calendarEvent', icon, title, message, calendarEvent);
			};
			try {
				this.controller.createStageWithCallback({
					name: calendarEventDashboardStage,
					lightweight: true,
					icon: icon
				}, pushDashboard, 'dashboard');
			}
			catch (error) {
				if (error.message.indexOf("cannot create two stages with the same name") != -1) {
					// dates over several days create several events with same id
					Mojo.Log.info('--- AppAssistant.createCalendarEventDashboard: found multiple events with same id: ' + calendarEvent.id);
					return;
				}
				else {
					throw error;
				}
			}
			this.calendarEvents.push(calendarEvent);
		}
	}
	catch (e) {
		Mojo.Log.error('--- ERROR: AppAssistant.createCalendarEventDashboard()', e);
	}
}

AppAssistant.prototype.calcNowThenUTC = function(){
	Mojo.Log.info('--- BEGIN AppAssistant.calcNowThenUTC()');
	try {
		this.calcTodayStartUTC();
		this.calcNowUTC();
		this.calcThenUTC();
	}
	catch (e) {
		Mojo.Log.error('--- ERROR: AppAssistant.calcNowThenUTC()', e);
	}
}

AppAssistant.prototype.calcNowUTC = function(){
	Mojo.Log.info('--- BEGIN AppAssistant.calcNowUTC()');
	try {
		var nowDate = new Date();
		this.nowUTC = Date.UTC(nowDate.getUTCFullYear(), nowDate.getUTCMonth(), nowDate.getUTCDate(),
				nowDate.getUTCHours(), nowDate.getUTCMinutes());
	}
	catch (e) {
		Mojo.Log.error('--- ERROR: AppAssistant.calcNowUTC()', e);
	}
}

AppAssistant.prototype.calcThenUTC = function(){
	Mojo.Log.info('--- BEGIN AppAssistant.calcThenUTC()');
	try {
		var thenDate = new Date();
		this.thenUTC = this.calcDate(thenDate, Preferences.upcomingDays, false);
	}
	catch (e) {
		Mojo.Log.error('--- ERROR: AppAssistant.calcThenUTC()', e);
	}
}

AppAssistant.prototype.calcTodayStartUTC = function(){
	Mojo.Log.info('--- BEGIN AppAssistant.calcTodayStartUTC()');
	try {
		var todayStartDate = new Date();
		this.todayStartUTC = this.calcDate(todayStartDate, 0, true);
	}
	catch (e) {
		Mojo.Log.error('--- ERROR: AppAssistant.calcTodayStartUTC()', e);
	}
}

AppAssistant.prototype.calcDate = function (date, days, startOfDay) {
	Mojo.Log.info("--- BEGIN AppAssistant.calcDate()");
	
	try {
		newDate = new Date(date);
		// to show events of whole day
		if (startOfDay) {
			newDate.setHours(0, 0, 0, 0);
		}
		else {
			newDate.setHours(23, 59, 59, 999);
		}
		
		dateUTC = Date.UTC(newDate.getUTCFullYear(), newDate.getUTCMonth(), newDate.getUTCDate(),
					newDate.getUTCHours(), newDate.getUTCMinutes());
		
		dateUTC += days * 24 * 60 * 60 * 1000;
		
		return dateUTC;
	} catch (e) {
		Mojo.Log.error("--- ERROR: AppAssistant.calcDate", e);
	}
}

AppAssistant.prototype.removeCalendarEventFromList = function (params) {
	Mojo.Log.info("--- BEGIN AppAssistant.removeCalendarEventFromList()");
	
	try {
		for (var i = 0; i < this.calendarEvents.length; i++) {
			var event = this.calendarEvents[i];
			if (event == params.event) {
				this.calendarEvents.splice(i, 1);
				this.calenderEventsToHide.push(event);
				break;
			}
		}
	} catch (e) {
		Mojo.Log.error("--- ERROR: AppAssistant.removeCalendarEventFromList", e);
	}
}

AppAssistant.prototype.eventLimitReached = function (events) {
	Mojo.Log.info("--- BEGIN AppAssistant.eventLimitReached()");
	
	try {
		if (Preferences.maximumEvents == 0) {
			return false;
		}
		return (events.length == Preferences.maximumEvents);		
	} catch (e) {
		Mojo.Log.error("--- ERROR: AppAssistant.eventLimitReached", e);
	}
}

AppAssistant.prototype.getCalendars = function (params) {
	Mojo.Log.info("--- BEGIN AppAssistant.getCalendars()");

	new Mojo.Service.Request('palm://com.palm.calendar', {
	    method:   'getCalendarsByAccount',
	    parameters: {
			"subscribe": false
		},
	    onSuccess: this.gotCalendars.bind(this),
	    onFailure: function(events) {
		 	Mojo.Log.info("Failure!!!!!!! " + events.errorCode + " " + events.errorText);
			Mojo.Controller.errorDialog($L("Failure!!!!!!! Cannot read Calendars."));
		}
	});
}

AppAssistant.prototype.gotCalendars = function(response){
	Mojo.Log.info("--- BEGIN AppAssistant.gotCalendars()");
	
	Preferences.calendars = [];
	// loop through accounts
	for (var i = 0; i < response.accounts.length; i++) {
		// loop through calendars
		for (var j = 0; j < response.accounts[i].calendars.length; j++) {
			var thisCal = response.accounts[i].calendars[j];
			thisCal.display = true;
			thisCal.id = 'id' + thisCal.id;
			for (var k = 0, len = Preferences.calendarsToHide.length; k < len; k++) {
				if (thisCal.id == Preferences.calendarsToHide[k]) {
					thisCal.display = false
					break;
				}
			}
			Preferences.calendars.push(thisCal);
		}
	}
	
	if (this.fromLauncher) {
		this.fromLauncher = false;
		this.openPreferences();
		
		// create preferences dashboard entry
		this.createPreferencesDashboard("icon.png", "UpDates", "Preferences");
	}
	else {
		this.getEvents();
	}
}

